import {
  CENTEREVENTS,
  DocumentEvents,
  EventsRes,
  Icon,
  ICurve,
  MouseInfo,
  sketchUtils,
} from '@amcax/base';
import { ctx2, userEvents } from '@app-cad/module';
import { csEvent } from '@app-cad/common/events/EventCenter';
import { useTools } from '@app-cad/common/hooks/useTools';
import { useMemoizedFn, usePrevious } from 'ahooks';
import { useEffect, useMemo, useRef, useState } from 'react';
import './style.scss';
import {
  MouseActionInput,
  MouseActionInputProps,
} from '@app-cad/common/components/pick-input/mouse-action-input';
import { PickInputEvents } from './types';

export type Base2DPickInputProps = MouseActionInputProps & {
  value?: string[];
  onPick?: (ids: string[], picked?: ICurve[]) => void;
  // 完全受控，value为null时视为置空，仅在外部value改变时试图绘制preview
  controlled?: boolean;
  isMulti?: boolean;
  disabled?: boolean;
  placeholder?: string;
  allowMarqueeSelect?: boolean;
};

const getName = (picked: ICurve) => {
  if (picked == null) {
    return '';
  }

  // 特殊处理轴系和原点
  const nameMap = {
    'X-AXIS': 'X轴',
    'Y-AXIS': 'Y轴',
    ORIGIN: '原点',
  };

  const nameFromMap = nameMap[picked.id];

  if (nameFromMap) {
    return nameFromMap;
  }

  if ((picked as any).label != null) {
    return (picked as any).label;
  }

  return getName(picked.parent);
};

export const useMutiPick2D = (props: Base2DPickInputProps) => {
  const {
    onPick,
    value,
    controlled,
    isMulti = false,
    disabled = false,
    active,
    setActive,
    allowMarqueeSelect = true,
  } = props;

  const [innerIsActive, setInnerIsActive] = useState(false);

  const isActive = active ?? innerIsActive;

  const setIsActive = (val: boolean) => {
    (setActive ?? setInnerIsActive)(val);
  };

  // state允许作为非受控组件使用
  const [innerPickedObjects, setInnerPickedObjects] = useState<ICurve[]>([]);

  const renderObjs: ICurve[] = useMemo(() => {
    if (value == null) {
      return null;
    }

    const list = [...ctx2.cadScene.sketchGroup.children];

    ctx2.cadScene.sketchGroup.children.forEach((v) => {
      if (v.children?.length > 0) {
        list.push(...v.children);
      }
    });

    const result = list
      .map<ICurve>((v) => v.userData?.info)
      .filter((v) => v != null && value?.includes(v.id));

    return isMulti ? result : result.slice(0, 1);
  }, [value]);

  const pickedData = controlled
    ? (renderObjs ?? [])
    : (renderObjs ?? innerPickedObjects);

  const { picker2d } = useTools();

  const pickByEvent = (e: EventsRes<PointerEvent>) => {
    return picker2d.pick(e);
  };

  const marqueeFlagRef = useRef(false);

  const onMouseMove = useMemoizedFn((e: EventsRes<PointerEvent>) => {
    if (e.button?.onLeft) {
      if (isMulti && allowMarqueeSelect) {
        picker2d.marqueeSelection(e);
        marqueeFlagRef.current = true;
      }
    } else {
      marqueeFlagRef.current = false;
      pickByEvent(e);
    }
  });

  const clearPreview = () => {
    picker2d.clickNull();
    ctx2.viewer.requestRender();
  };

  useEffect(() => clearPreview, []);

  const preview = () => {
    clearPreview();

    picker2d.selectModels(pickedData.map((v) => v.id));
  };

  const pointDownPositionRef = useRef<MouseInfo>();

  const onMouseDown = useMemoizedFn((e: EventsRes<PointerEvent>) => {
    picker2d.setPointerDownPos(e);
    pointDownPositionRef.current = e.mouse;
  });

  const onMouseUp = useMemoizedFn((e: EventsRes<PointerEvent>) => {
    const threshold = 20;
    const pointDownPosition = pointDownPositionRef.current;
    const distance = Math.sqrt(
      (e.mouse?.x - pointDownPosition?.x) ** 2 +
        (e.mouse?.y - pointDownPosition?.y) ** 2,
    );
    if (marqueeFlagRef.current && distance > threshold) {
      marqueeFlagRef.current = false;

      onMarqueePick(picker2d.getSelectedObjectInfo());
    } else {
      const pickRes = pickByEvent(e);

      if (pickRes == null) {
        return;
      }

      onPickNew(pickRes);
    }
  });

  const onMarqueePick = (target: ICurve[]) => {
    const newResult = [
      ...pickedData.filter((p) => target.every((t) => t.id !== p.id)),
      ...target,
    ];

    onPick?.(
      newResult.map((v) => v.id),
      newResult,
    );
    setInnerPickedObjects(newResult);
  };

  const onPickNew = (target: ICurve) => {
    const newResult: ICurve[] = isMulti
      ? pickedData.some((p) => p.id === target.id)
        ? pickedData.filter((p) => p.id !== target.id)
        : [...pickedData, target].filter((v) => v != null)
      : [target];

    onPick?.(
      newResult.map((v) => v.id),
      newResult,
    );
    setInnerPickedObjects(newResult);
  };

  useEffect(() => {
    clearPreview();
  }, []);

  const previousValue = usePrevious(value);

  useEffect(() => {
    if (!controlled && previousValue != null && value == null) {
      clearPreview();
    } else {
      preview();
    }
  }, [previousValue, pickedData, value, controlled]);

  const onTreeViewSelected = useMemoizedFn(
    async ({ originalObj }: { originalObj: ICurve }) => {
      if (originalObj?.id == null) {
        return;
      }

      onPickNew({
        ...originalObj,
        id: sketchUtils.getCurveIdsFromCurveChildDxid(originalObj.id).at(-1),
      });
    },
  );

  const unRegister = useMemoizedFn(() => {
    setIsActive(false);

    csEvent()
      .off(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .off(CENTEREVENTS.POINTERUP, onMouseUp)
      .off(CENTEREVENTS.POINTERDOWN, onMouseDown);

    userEvents.off(PickInputEvents.Input2dFocused, unRegister);

    userEvents.off(DocumentEvents.SELECT_TREE_OBJECT, onTreeViewSelected);
  });

  const register = () => {
    unRegister();

    userEvents.emit(PickInputEvents.Input2dFocused);
    userEvents.on(PickInputEvents.Input2dFocused, unRegister);
    if (disabled) {
      return;
    }

    setIsActive(true);

    csEvent()
      .on(CENTEREVENTS.POINTERMOVE, onMouseMove)
      .on(CENTEREVENTS.POINTERUP, onMouseUp)
      .on(CENTEREVENTS.POINTERDOWN, onMouseDown);

    userEvents.on(DocumentEvents.SELECT_TREE_OBJECT, onTreeViewSelected);
  };

  return {
    register,
    unRegister,
    isActive,
    pickedData,
    setInnerPickedObjects,
  };
};

export function Multi2DPickInput(props: Base2DPickInputProps) {
  const {
    onPick,
    value,
    controlled,
    isMulti = false,
    disabled = false,
    active,
    setActive,
    allowMarqueeSelect = true,
    ...rest
  } = props;

  const { register, unRegister, isActive, pickedData, setInnerPickedObjects } =
    useMutiPick2D({
      onPick,
      value,
      controlled,
      isMulti,
      disabled,
      active,
      setActive,
      allowMarqueeSelect,
    });

  return (
    <MouseActionInput
      onActiveFocus={register}
      onActiveBlur={unRegister}
      disabled={disabled}
      allowSelectFromTreeView
      {...rest}
      active={isActive}
    >
      {pickedData.map((data) => {
        const name = getName(data);

        return (
          <div
            key={data.id}
            className='pick-input_base-select_selection'
          >
            <div
              className='break-all'
              title={name}
            >
              {name}
            </div>

            <div
              className='ml-auto'
              onClick={() => {
                const newResult = pickedData.filter((p) => data.id !== p.id);
                onPick?.(
                  newResult.map((v) => v.id),
                  newResult,
                );
                setInnerPickedObjects(newResult);
              }}
            >
              <Icon
                tabIndex={0}
                name='commons/close'
              />
            </div>
          </div>
        );
      })}
    </MouseActionInput>
  );
}
